/**
 *******************************************************************************
 * @file       mmu.c 
 * @author     camel.shoko
 * @version    v1.1
 * @date       2014/05/26
 * @brief      S3C2416 MMU驱动
 * @copyright  camel.shoko@gmail.com
 *******************************************************************************
 */
 

/* Defines -------------------------------------------------------------------*/
#define MMU_IO_AREA_START							(0x00000000)

#define MMU_RESERVED1_START							(0x60000000)
#define MMU_MEM_AREA_START							(0xc0000000)

#define LCD_DMA_BUFFER_PA_BASE  					(0x33800000)
#define LCD_DMA_BUFFER_PA_END   					(0x33b00000)

#define MMU_RESERVED2_START							(0xc4000000)
#define MMU_EXCEPTION_START							(0xffff0000)

#define MMU_PAGE_TABLE_START   						(0x30100000)
#define MMU_PHYSICAL_EXCEPTION_START				(0x30000000)

#define MMU_MEM_AREA_START_3						(0x30000000)
#define MMU_MEM_AREA_START_3_4						(0x34000000)


/*
	虚地址0x00000000-0x30000000映射到0x00000000-0x30000000
	虚地址0x34000000-0x60000000映射到0x34000000-0x60000000
	虚地址0xc0000000-0xc4000000映射到0x30000000-0x34000000
	虚地址0x60000000-0xc0000000禁止访问
	虚地址0xc4000000-0xffff0000禁止访问
	虚地址0xffff0000-0xffffffff映射到0x30000000-0x30100000
	
	0x30000000-0x30100000---异常向量表使用
	0x30100000-0x30200000---MMU的PAGE_TABLE使用
	0x33800000-0x33b00000---LCD的DMA缓冲使用
	0x33b00000-0x34000000---CACHE没有打开供给外设DMA使用
	0x30200000-0x30800000---可用于堆栈
*/


/* Macros --------------------------------------------------------------------*/

/*
NOTE:
	http://blog.sina.com.cn/s/blog_9447904001015h38.html
	http://www.it165.net/embed/html/201408/2633.html
	
	DDI0198E_arm926ejs_r0p5_trm.pdf
*/

#define SECTION_ENTRY(base, ap, d, c, b) 				\
	((base << 20)|(ap<<10)|(d<<5)|(1<<4)|(c<<3)|(b<<2)|(1<<1))


#define SECTION_ENTRY_CACHE(base, tex, ap, d, c, b) 	\
	((base << 20)|(tex << 12)|(ap<<10)|(d<<5)|(1<<4)|(c<<3)|(b<<2)|(1<<1))


/* Global Functions ----------------------------------------------------------*/

/**
 * @brief  make_mmu_table
 * @note   mmu表初始化
 * @param  none
 * @retval none
 */
 
void make_mmu_table(void)
{
	int i;
	
	unsigned int *addr = (unsigned int *)MMU_PAGE_TABLE_START;

	
	/* 虚地址0x00000000-0x30000000映射到0x00000000-0x30000000 */
	for ( i=(MMU_IO_AREA_START>>20); i<(MMU_MEM_AREA_START_3>>20); i++ ) {
		
		addr[i] = SECTION_ENTRY(i,1,1,0,0);
	}
	
	
	/*
		1)WRITE THROUGH
		CPU向CACHE写入数据时,同时向MEMORY也写一份,使CACHE和MEMORY的数据保持一致.
		优点是简单,缺点是每次都要访问MEMORY速度比较慢.

		2)POST WRITE
		CPU更新CACHE数据时,把更新的数据写入到一个更新缓冲器,在合适的时候才对MEMORY进行更新.
		这样可以提高CACHE访问速度,但是在数据连续被更新两次以上的时候,缓冲区将不够使用,被迫同时更新MEMORY.

		3)WRITE BACK
		CPU更新CACHE时,只是把更新的CACHE区标记一下,并不同步更新MEMORY.
		只是在CACHE区要被新进入的数据取代时才更新MEMORY,考虑到很多时候CACHE存入的是中间结果,没有必要同步更新MEMORY.
		优点是CPU执行的效率提高,缺点是实现起来技术比较复杂.
	*/
	
	/* 映射WRITE BACK CACHE从0x30000000-0x33800000 */
	for ( i=(MMU_MEM_AREA_START_3>>20); i<(LCD_DMA_BUFFER_PA_BASE >> 20); i++ ) {
			
		addr[i] = SECTION_ENTRY_CACHE(i,0,1,1,1,1);	
	}


	/* 映射WRITE THROUGH CACHE从0x33800000-0x33b00000 */
	for ( i=(LCD_DMA_BUFFER_PA_BASE>>20); i<(LCD_DMA_BUFFER_PA_END >> 20); i++ ) {

		addr[i] = SECTION_ENTRY_CACHE(i,0,1,1,1,0);
	}


	/* 地址0x33b00000-0x34000000未被作为CACHE使用,可用于硬件DMA */
	for ( i=(LCD_DMA_BUFFER_PA_END>>20); i<(MMU_MEM_AREA_START_3_4 >> 20); i++ ) {
		
		addr[i] = SECTION_ENTRY_CACHE(i,0,1,1,0,0);
	}


	
	/* 虚地址0x34000000-0x60000000映射到0x34000000-0x60000000 */
	for ( i=(MMU_MEM_AREA_START_3_4>>20); i<(MMU_RESERVED1_START>>20); i++ ) {
		
		addr[i] = SECTION_ENTRY(i,1,1,0,0);
	}

	
	
	/* 虚地址0x60000000-0xc0000000禁止访问 */
	for ( i=(MMU_RESERVED1_START>>20); i<(MMU_MEM_AREA_START>>20); i++ ) {
		
		addr[i] = 0x00000000;
	}

	
	
	/* 系统内存被映射到0xc0000000-0xc4000000 */
	for ( i=(MMU_MEM_AREA_START>>20); i<(MMU_RESERVED2_START>>20); i++ ) {
		
		addr[i] = SECTION_ENTRY((i-((MMU_MEM_AREA_START>>20)-(MMU_MEM_AREA_START_3>>20))),1,1,0,0);
	}

	
	
	/* 虚地址0xc4000000-0xffff0000禁止访问,虚地址0xffff0000-0xffffffff映射到0x30000000-0x300fffff */
	for ( i=(MMU_RESERVED2_START>>20); i<0x1000; i++ ) {

	#if ENABLE_EXCEPTION_MMU
		if ( i >= (MMU_EXCEPTION_START >> 20) ) {
			addr[i] = SECTION_ENTRY( (i-((MMU_EXCEPTION_START>>20)-(MMU_PHYSICAL_EXCEPTION_START>>20))), 1, 1, 0, 0);
		} else {
			/* 禁止访问 */
			addr[i] = 0x00000000;
		}
	#else
			if ( i == (MMU_EXCEPTION_START >> 20) ) {
			addr[i] = SECTION_ENTRY( (i-((MMU_EXCEPTION_START>>20)-(MMU_PHYSICAL_EXCEPTION_START>>20))), 1, 1, 0, 0);
			break;
		} else {
			/* 禁止访问 */
			addr[i] = 0x00000000;
		}
	#endif
	}
	
}

/****************************** END OF FILE ***********************************/
